using System;
using System.Collections;
using System.Data;
using System.DirectoryServices;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Threading;
using System.Web;
using Microsoft.ApplicationBlocks.Data;
using Config = Dimok.Config;
using Dimok.Data;
using Dimok.Exceptions;
using Dimok.Data.Database;

namespace Dimok.User{

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Get information common for every user. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class UserObjectBase : System.Web.Services.WebService{
		private static Hashtable usersCache = Hashtable.Synchronized(new Hashtable());

		#region CONSTANTS ================
		protected const int APPL_ID_UNDEFINED = 0;
		protected const int MENU_ID_UNDEFINED = 0;
		public static readonly string APPL_ID_PARAM = "ApplID";
		public const string MENU_ID_PARAM = "MenuID";
		public const string NAMESPACE_EMPTY = "";
		#endregion CONSTANTS ================

		#region Member Properties ==============
		private string userPath = null;
		private string[] _groups = null;
		private string _group = null;
		protected int m_applID = APPL_ID_UNDEFINED;
		protected int m_menuID = MENU_ID_UNDEFINED;
		#endregion Member Properties ==============

		#region Constructors ============
		public UserObjectBase(){}
		public UserObjectBase(string userPath){
			this.userPath = userPath;
		}
		#endregion Constructors ============

		#region Public Members ==============

		#region Query Params ================
		public static string QueryString{get{return HttpContext.Current.Request.RawUrl;}}
		public static int GetQueryParamInt(string paramName){
			string param = GetQueryParam(paramName);
			return param == "" ? 0 : int.Parse(param);
		}
			/// <summary>
		/// Get parameter from query string
		/// </summary>
		/// <param name="paramName"></param>
		/// <returns>Parameter or empty string</returns>
		public static string[] GetQueryParams(string paramName){
			return HttpContext.Current.Request.QueryString.GetValues(paramName);
		}
		public static string GetQueryParam(string paramName){
			return (HttpContext.Current.Request[paramName]+"");//.Split(',')[0];
			//return param != null ? param : "";
		}
		public static int applIDParam{
			get{
				string aid = GetQueryParam(APPL_ID_PARAM);
				return aid.Length > 0 ? int.Parse(aid) : APPL_ID_UNDEFINED;
			}
		}
		public int menuIDParam{
			get{
				string mid = GetQueryParam(MENU_ID_PARAM);
				return mid.Length > 0 ? int.Parse(mid) : MENU_ID_UNDEFINED;
			}
		}
		public bool hasMenuID{get{return menuIDParam != MENU_ID_UNDEFINED;}}
		public class QueryParam{
			public static string Get(string paramName){
				string param = HttpContext.Current.Request[paramName];
				return param != null ? param : "";
			}
			public static string Search{
				get{
					try{return HttpContext.Current.Request.RawUrl.Split('?')[1];
					}catch{return "";}
				}
			}
			public static string VIN{get{return Get("VIN");}}
			public static bool IsFax{get{return Get("IsFax") != "";}}
			public static string FaxUser{get{return Get("FaxUser");}}
			public static string Title{get{return Get("Title");}}
			public static string PopUpID{get{return Get("PopUpID");}}
		}
		#endregion Params

		public virtual int applID{
			get{
				if( m_applID != APPL_ID_UNDEFINED )return m_applID;
				if( !hasApplID )throw new NoApplicantIDException();
				return applID = applIDParam;
			}
			set{m_applID = value;}
		}
		public bool hasApplID {get{return applIDParam != APPL_ID_UNDEFINED || m_applID != APPL_ID_UNDEFINED;}}
		public int ApplID0 {get{return hasApplID ? applID : 0;}}
		public string BuildPageURL(object pageURL){return BuildPageURL(pageURL,hasApplID?applID:0);}
		public string BuildPageURL(object pageURL,int applID){
			return pageURL.ToString().ToLower().Replace("applid=",applID!=0?"applid="+applID:"");
		}
		#endregion Public Members ==============

		#region NAME_SPACE ==============
		// Web_AFC_Dealer_Sales
		// <-----> 
		protected static string AppSpace{get{return Config.application.AppSpace;}}// AFC
		protected static string WebSpace{get{return "Web_"+AppSpace;}}// Web_AFC
		private string NameSpaceFromDN{
			get{
				return !Regex.Match(LdapUser.Path,"OU="+AppSpace+",OU=WEB",RegexOptions.IgnoreCase).Success ?
							 NAMESPACE_EMPTY : LdapUser.Parent.Name.Split('=')[1];
			}
		}

		protected virtual string NameSubSpace{get{return "-1";}}
		//private static string GetOUName(string ou){return Regex.Replace(ou,".+=","");}
		#endregion NAME_SPACE ==============

		#region User/Groups ===============
		public bool IsInDomain{get{return ADs.isInDomain(UserPath);}}
			public DirectoryEntry LdapUser{
			get{
				if( usersCache.Contains(UserPath) )return (DirectoryEntry)usersCache[UserPath];
				usersCache.Add(UserPath,ADs.GetUser(UserPath));
				return LdapUser;
			}
		}
		public virtual string[] Groups{
			get{
				if(_groups == null) _groups = new string[] { "Admins" };//DDD GetUserGroups(false);
				return _groups;
			}
		}
		/*
		public string Group{
			get{
				if( _group == null )_group = Groups[0];
				return _group.Split('_')[_group.Split('_').Length-1];
			}
		}
		*/

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the name of the user. </summary>
		///
		/// <value>	The name of the user. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string UserName {get{return UserPath.Split('\\')[1];}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the user domain. </summary>
		///
		/// <value>	The user domain. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string UserDomain {get{return UserPath.Split('\\')[0];}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the full pathname of the user file. </summary>
		///
		/// <value>	The full pathname of the user file. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string UserPath{
			get{
				if( userPath == null )userPath = CurrentUserPath;
				return userPath;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the current user name. </summary>
		///
		/// <value>	The name of the current user. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string CurrentUserName {get{return CurrentUserPath.Split('\\')[1];}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the current user domain. </summary>
		///
		/// <value>	The current user domain. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string CurrentUserDomain {get{return CurrentUserPath.Split('\\')[0];}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="propertyName">	The string to get. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public object Get(string propertyName){
			return ADs.GetProperty(LdapUser,propertyName);
			//PropertyValueCollection pvc = LdapUser.Properties[propertyName];
			//return pvc == null || pvc.Count == 0 ? null : pvc[0];
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the full pathname of the current user file. </summary>
		///
		/// <value>	The full pathname of the current user file. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string CurrentUserPath{
			get{
				string s = "";
				try{
					s = HttpContext.Current.User.Identity.Name;
				}catch{}
				if( s == "" || s == null )s =	Thread.CurrentPrincipal.Identity.Name;
				if( s == "" || s == null )s =	WindowsIdentity.GetCurrent().Name;
				return s;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Extracts Group Name from Group's Short DN. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="groupShDN">	Short DN. </param>
		///
		/// <returns>	Pure group name. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected static string GroupNameExtract(string groupShDN){
			string groupName = groupShDN.Split('=')[groupShDN.Split('=').Length-1];
			groupName = Regex.Match(groupName,"[^_]+$").Value;//CN=Web_AppSpace_NameSpace_Group
			return			Regex.Match(groupName,"^[^{]+").Value;//CN=Group{432098402-32908423-4323-342234-0980900}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a user groups. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="Exception">	Thrown when exception. </exception>
		///
		/// <param name="raw">	true to raw. </param>
		///
		/// <returns>	The user groups. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected virtual string[] GetUserGroups(bool raw){
			string[] aGroups = ADs.GetUserGroups(LdapUser,"(?:\\\\)?"+WebSpace);
			if( aGroups.Length == 0 )throw new Exception("You are not a member of any WEB group.\nGo see Administrator.");
			//for( int i=0; !raw && i < aGroups.Length; i++ )
			//	aGroups[i] = GroupNameExtract(aGroups[i]);
			return aGroups;
		}
		#endregion User/Groups ===============

		#region AD Shortcuts =============

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the state. </summary>
		///
		/// <value>	The state. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string State{
			get{
				object state = Get("st");
				return state == null ? "" : state.ToString();
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the extent of the display name and. </summary>
		///
		/// <value>	The extent of the display name and. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string DisplayNameAndExt{get{return DisplayName+" Ext:"+TelephoneExtention;}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the name of the display. </summary>
		///
		/// <value>	The name of the display. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string DisplayName{
			get{
				if( GetQueryParam("displayName") != "" )
					return GetQueryParam("displayName");
				if( !IsInDomain )return UserName;
				object prop = Get("displayName");
				return prop == null ? "" : prop.ToString();
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the telephone number. </summary>
		///
		/// <value>	The telephone number. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string TelephoneNumber{
			get{
				object prop = Get("telephoneNumber");
				return prop == null ? "" : prop.ToString();
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the telephone extention. </summary>
		///
		/// <value>	The telephone extention. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public virtual string TelephoneExtention{get{return ADs.TelephoneExtention(LdapUser);}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the extention. </summary>
		///
		/// <value>	The extention. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string Extention{
			get{
				string ext = Get("info")+"";
				if( ext == "" )ext = ADs.TelephoneExtention(LdapUser);
				return Regex.Replace(ext,"ext[^d]*","",RegexOptions.IgnoreCase);
			}
		}
		#endregion

		#region Cloner

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Makes a deep copy of this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="o">	The. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public void Clone(object o){ Clone(null,o);	}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Makes a deep copy of this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="type">	The type. </param>
		/// <param name="o">		The. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void Clone(Type type,object o){
			if( type == null )type = this.GetType();
			BindingFlags flags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public;
			Type oType = MatchType(type,o);
			foreach( FieldInfo fi in type.GetFields(flags) )
				oType.GetField(fi.Name,flags).SetValue(o,fi.GetValue(this));
			if( type.BaseType != null )Clone(type.BaseType,o);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Match type. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="type">	The type. </param>
		/// <param name="o">		The. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Type MatchType(Type type,object o){
			Type oType = o.GetType();
			for(; oType != null && type != oType; oType = oType.BaseType ){}
			return oType;
		}
		#endregion
	}
}
